Skip to content

feat(platform): support external token providers and simplify caching [PYSDK-123]#513

Open
akunft wants to merge 3 commits intomainfrom
feat/enable_external_token_provider
Open

feat(platform): support external token providers and simplify caching [PYSDK-123]#513
akunft wants to merge 3 commits intomainfrom
feat/enable_external_token_provider

Conversation

@akunft
Copy link
Copy Markdown
Collaborator

@akunft akunft commented Mar 26, 2026

🛡️ Implements PYSDK-123 following CC-SOP-01 Change Control, part of our ISO 13485-certified QMS | Ketryx Project

Why

Add an external token_provider parameter to Client.__init__() so callers can supply their own bearer-token callable (e.g. machine-to-machine / service-account flows) instead of relying on the built-in OAuth device-code flow. Internal OAuth remains the default; with this change it is no longer the only supported path.

Primary motivation: enable headless deployment scenarios (CI/CD jobs, Auth0 M2M, custom auth backends) where the device-code browser flow is impractical or impossible. Driving use case is M2M tokens cached upstream in Redis with stale-while-validate semantics.

What

SIS — specifications/SPEC_PLATFORM_SERVICE.md:

  • New FR-14: "Support external token providers to bypass internal OAuth 2.0 flows for machine-to-machine, service account, or custom token lifecycle scenarios."
  • Constraint relaxed: the "Browser dependency: Interactive flow requires web browser availability, limiting headless deployment options" line is removed because external token_provider enables headless operation.
  • Strategy pattern updated to describe external-token-provider as a fully independent alternative auth strategy.
  • Auth flow Mermaid diagram updated with the external-provider branch.
  • Outputs table & class signatures updated for the new token_provider parameter and the internal _AuthenticatedApi return type.

Code — src/aignostics/platform/:

  • New file _api.py introducing internal _AuthenticatedApi (PublicApi subclass that hoists token_provider to a top-level attribute), _OAuth2TokenProviderConfiguration, and _AuthenticatedResource base class.
  • Client.__init__(cache_token=True, token_provider=None) — additive optional kwarg.
  • Client.get_api_client() — same kwarg; returns _AuthenticatedApi. Three singleton-cache pools: cached-token, uncached-token, external-provider (bounded at 16 entries with clear-on-overflow safety net to address Sentry's flagged unbounded-growth concern).
  • cached_operation decorator — use_token: bool replaced by token_provider: Callable[[], str] | None, so per-user cache key isolation is wired through the explicit provider rather than reaching into the global get_token().

What does NOT change

  • No backend / API-server change.
  • Internal OAuth device-code and authorization-code flows continue to work unchanged when token_provider is not supplied.
  • No change to aignostics user login, token caching at ~/.aignostics/token.json, or refresh-token semantics.
  • No change to existing public method signatures other than the additive token_provider kwarg.
  • Backwards-compatible — existing callers continue to work without modification.

Risks introduced

None material. The new parameter is opt-in. The bounded _api_client_external cache addresses Sentry's flagged unbounded-growth concern. No patient-safety, security, or data-integrity risk identified — the external token continues to flow through the same Authorization: Bearer … header path; the SDK never inspects, persists, or logs the token value.

Test plan

  • make lint green (ruff, format, pyright, mypy)
  • make test_unit green; new tests/aignostics/platform/client_token_provider_test.py covers external-provider wiring
  • Cache-test suite refactored to mock token_provider callable rather than patching get_token — exercises the new key-derivation path
  • Codecov on patch
  • CodeQL clean; SonarQube quality gate passed
  • Required Testing = Verification only — automated CI proves the surface area; the headless-M2M path itself will be exercised when a downstream caller integrates it (out of scope for this CR)

Note: Originally provided retroactively for PYSDK-112 (Helmut); governing ticket corrected to PYSDK-123 (Andreas, Claude claude-sonnet-4-6 via cc-sop-01).

Copilot AI review requested due to automatic review settings March 26, 2026 16:38
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for supplying an external bearer-token provider to the Platform Client, and refactors operation caching to key per-user via an explicit token_provider callable rather than relying on a global auth helper.

Changes:

  • Introduces _AuthenticatedApi + _OAuth2TokenProviderConfiguration in a new _api.py to surface token_provider as a top-level attribute (avoiding circular imports).
  • Updates cached_operation to accept token_provider (replacing the previous use_token flag) and wires it through Client and resource modules.
  • Updates and expands unit tests to cover external token providers and the revised caching behavior.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/aignostics/platform/_api.py Adds _AuthenticatedApi wrapper + configuration class to lift token_provider out of codegen internals.
src/aignostics/platform/_client.py Adds token_provider parameter to Client, introduces external-provider singleton caching, and passes token_provider into cached_operation.
src/aignostics/platform/_operation_cache.py Replaces use_token with token_provider in the caching decorator API and key generation.
src/aignostics/platform/resources/runs.py Updates resource API type hints and passes token_provider=self._api.token_provider into cached operations.
src/aignostics/platform/resources/applications.py Same as above for applications/versions resources.
tests/aignostics/platform/client_token_provider_test.py Adds unit tests for external token providers and adjusts existing tests to the new API wiring.
tests/aignostics/platform/client_cache_test.py Updates cache tests to use mocked token_provider for token-aware cache keys.
tests/aignostics/platform/nocache_test.py Updates decorator calls to the new cached_operation signature.
tests/aignostics/platform/conftest.py Ensures new external-client singleton cache is cleared between tests; adds token_provider to mocked API clients.
tests/aignostics/platform/resources/runs_test.py Updates resource mocks to include token_provider/api_client attributes.
tests/aignostics/platform/resources/applications_test.py Updates resource mocks to include token_provider/api_client attributes.

Comment thread tests/aignostics/platform/resources/runs_test.py Outdated
Comment thread tests/aignostics/platform/resources/applications_test.py Outdated
Comment thread src/aignostics/platform/resources/runs.py
Comment thread src/aignostics/platform/resources/applications.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 26, 2026

Codecov Report

❌ Patch coverage is 98.95833% with 1 line in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/aignostics/platform/resources/applications.py 91.66% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (ef28b07) and HEAD (bf956ae). Click for more details.

HEAD has 2 uploads less than BASE
Flag BASE (ef28b07) HEAD (bf956ae)
3 1
Files with missing lines Coverage Δ
src/aignostics/platform/_api.py 100.00% <100.00%> (ø)
src/aignostics/platform/_authentication.py 77.92% <100.00%> (-0.40%) ⬇️
src/aignostics/platform/_client.py 93.02% <100.00%> (-0.67%) ⬇️
src/aignostics/platform/_operation_cache.py 78.43% <100.00%> (+0.88%) ⬆️
src/aignostics/platform/resources/runs.py 83.58% <100.00%> (-1.21%) ⬇️
src/aignostics/platform/resources/applications.py 84.52% <91.66%> (+1.28%) ⬆️

... and 21 files with indirect coverage changes

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread tests/aignostics/platform/resources/runs_test.py Outdated
Comment thread tests/aignostics/platform/resources/applications_test.py Outdated
Comment thread src/aignostics/platform/_client.py Outdated
@akunft akunft force-pushed the feat/enable_external_token_provider branch from ab1533d to b407d30 Compare March 30, 2026 13:41
@akunft akunft requested a review from Copilot March 30, 2026 13:42
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Comment thread src/aignostics/platform/_client.py Outdated
Comment thread src/aignostics/platform/resources/runs.py Outdated
Comment thread src/aignostics/platform/_api.py Outdated
Comment thread src/aignostics/platform/_client.py Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Comment thread src/aignostics/platform/_client.py
Comment thread src/aignostics/platform/_api.py
Comment thread tests/aignostics/platform/resources/runs_test.py
Comment thread tests/aignostics/platform/conftest.py
@akunft akunft force-pushed the feat/enable_external_token_provider branch from 7e0a0b9 to de2c81d Compare March 31, 2026 09:41
Comment thread src/aignostics/platform/_client.py
Copilot AI review requested due to automatic review settings March 31, 2026 12:31
@akunft akunft force-pushed the feat/enable_external_token_provider branch from de2c81d to 7e3fac5 Compare March 31, 2026 12:31
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread src/aignostics/platform/_api.py
Comment thread src/aignostics/platform/_client.py Outdated
@akunft akunft requested a review from Copilot March 31, 2026 13:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/aignostics/platform/resources/runs.py:137

  • Run.for_run_id() always constructs its API client via Client.get_api_client(cache_token=...), so there is currently no way to use it with an external token_provider (the new auth path). Consider adding an optional token_provider parameter (and documenting cache_token as ignored when it’s provided) so this convenience constructor remains usable for M2M/service-account flows.
    @classmethod
    def for_run_id(cls, run_id: str, cache_token: bool = True) -> "Run":
        """Creates an Run instance for an existing run.

        Args:
            run_id (str): The ID of the application run.
            cache_token (bool): Whether to cache the API token.

        Returns:
            Run: The initialized Run instance.
        """
        from aignostics.platform._client import Client  # noqa: PLC0415

        return cls(Client.get_api_client(cache_token=cache_token), run_id)

Comment thread src/aignostics/platform/_api.py
Comment thread src/aignostics/platform/_operation_cache.py
Comment thread src/aignostics/platform/_client.py
@olivermeyer
Copy link
Copy Markdown
Collaborator

I believe this impacts https://github.com/aignostics/python-sdk/blob/main/specifications/SPEC_PLATFORM_SERVICE.md, therefore it needs a CR?

@arne-aignx
Copy link
Copy Markdown
Contributor

I believe this impacts https://github.com/aignostics/python-sdk/blob/main/specifications/SPEC_PLATFORM_SERVICE.md, therefore it needs a CR?

Yes, I concur...
I don't even think this is strictly covered by the requirements stated in that SIS.

-> SW requirement + SIS update is in order here :/

@akunft
Copy link
Copy Markdown
Collaborator Author

akunft commented Apr 2, 2026

I believe this impacts https://github.com/aignostics/python-sdk/blob/main/specifications/SPEC_PLATFORM_SERVICE.md, therefore it needs a CR?

Yes, I concur... I don't even think this is strictly covered by the requirements stated in that SIS.

-> SW requirement + SIS update is in order here :/

Agreed. I will look into this, but only on Monday.

@akunft akunft force-pushed the feat/enable_external_token_provider branch from f2296b9 to 262ac88 Compare April 7, 2026 13:54
Comment thread specifications/SPEC_PLATFORM_SERVICE.md Outdated
Copilot AI review requested due to automatic review settings April 10, 2026 16:22
helmut-hoffer-von-ankershoffen added a commit that referenced this pull request Apr 26, 2026
…ows [PYSDK-112]

`test_no_retry_on_other_jwk_errors` had two assertions checking the same
property (no retry occurred):

  1. `assert elapsed_time < 2.0` — wall-clock heuristic, flaky
  2. `assert call_count == 1` — direct check on the mock's call count

Assertion 2 proves "no retry" rigorously and deterministically. Assertion
1 was a redundant proxy that consistently failed on `windows-latest`
GitHub-hosted runners (came in at ~2.4s vs the 2s threshold) while the
other four runner combos (ubuntu-latest, ubuntu-24.04-arm, macos-latest,
macos-15-intel) all completed in well under 2s.

Removing assertion 1 (and the now-unused `start_time`/`elapsed_time`
locals) removes the flake without losing test coverage. The companion
test `test_jwk_connection_errors_trigger_retry` immediately above already
follows this pattern (call-count assertion only, no wall-clock check).

Bundled into PYSDK-112 at user direction — surfaced on PR #513 CI run
and observed as the only blocker for the otherwise-green test matrix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 26, 2026 12:50
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Comment thread specifications/SPEC_PLATFORM_SERVICE.md
Comment thread src/aignostics/platform/_client.py
@helmut-hoffer-von-ankershoffen
Copy link
Copy Markdown
Contributor

Windows flake resolved ✅

Commit 4097283e (fix(tests): remove redundant wall-clock assertion that flaked on Windows) made it through CI cleanly.

Runner Before flake fix After flake fix
windows-latest ❌ fail (28m, Expected fast failure but took 2.37s) pass (27m36s)
ubuntu-latest ✅ pass ✅ pass (20m37s)
ubuntu-24.04-arm ✅ pass ✅ pass (22m11s)
macos-latest ✅ pass ✅ pass (25m28s)
macos-15-intel ✅ pass ⏳ still running (was passing before, no reason to expect different)

What changed and why this is rock-solid now

tests/aignostics/platform/authentication_test.py::TestTokenVerificationRetryLogic::test_no_retry_on_other_jwk_errors had two assertions checking the same property ("no retry happened"):

  1. assert elapsed_time < 2.0 — wall-clock heuristic (flaky on slow runners)
  2. assert call_count == 1 — direct check on the mock's call count (deterministic)

Assertion 2 already proves "no retry" rigorously — the timing assertion was a redundant proxy for the same property. The diff dropped assertion 1 (and the now-unused start_time/elapsed_time locals); assertion 2 stays. The companion test test_jwk_connection_errors_trigger_retry immediately above already follows this pattern (call-count only, no wall-clock).

Cumulative state of this PR

  • 5 commits added by me total: c0c36706 (S1192 + TypeError tests), 372d0cfd (SHR/SWR-PLATFORM requirements), 7f371463 (S7632), 4097283e (this fix)
  • SonarQube: 0 open issues
  • All static-analysis CI: ✅ green
  • Test matrix: 4/5 ✅ green (incl. the formerly-flaky Windows row); macos-15-intel ⏳ pending its own slow self
  • The follow-up /pr-sop-01 task chip I spun off earlier for this same flake is now obsolete — fix landed here. Helmut can dismiss it.

PYSDK-112's Impact of Change updated to record the bundled flake fix; comment posted on the ticket separately.


🤖 Posted by Claude Code executing the qms:cc-sop-01 skill (from the qms plugin) | model: claude-opus-4-7

@helmut-hoffer-von-ankershoffen helmut-hoffer-von-ankershoffen removed the skip:test:long_running Skip long-running tests (≥5min) label Apr 26, 2026
helmut-hoffer-von-ankershoffen added a commit that referenced this pull request Apr 26, 2026
CI run 24957873268 was triggered by the merge commit d9ab794 BEFORE
the skip:test:long_running label was removed from PR #513. As a result,
the workflow snapshotted the label as still present and skipped the
long-running test stage on every matrix combo (each long_running step
took 0s and was a no-op). Without long_running tests running, codecov
gathered partial coverage and codecov/project failed.

This empty commit forces a fresh workflow trigger that picks up the
current label set (skip:test:long_running no longer present), so
long_running tests will actually run and codecov gets full coverage data.
@helmut-hoffer-von-ankershoffen
Copy link
Copy Markdown
Contributor

helmut-hoffer-von-ankershoffen commented Apr 26, 2026

✅ Full CI green — PR ready for QMS approval gates

Run 24959853047 on commit 033e3e16:

Check Status
lint / lint ✅ pass
audit / audit ✅ pass
docs / docs ✅ pass
codeql / * ✅ pass
sonarcloud ✅ pass (0 issues)
codecov/patch ✅ pass (98.68%)
codecov/project pass (74.49%, target 70%)
test / test (ubuntu-latest, false) ✅ pass (34m22s)
test / test (ubuntu-24.04-arm, true) ✅ pass (28m4s)
test / test (macos-latest, true) ✅ pass (36m5s)
test / test (windows-latest, true) ✅ pass (36m27s)
test / test (macos-15-intel, true) ⏳ still running (slowest runner; trended fine on every prior run)

What it took to get codecov/project green

The skip:test:long_running label was suppressing the long_running test stage on every matrix combo, leaving codecov with partial coverage data and the project gate (≥70%) failing at 63.83%. Two false starts before the fix landed:

  1. Removing the label alone wasn't enough — the in-flight CI run had already snapshotted the labels at trigger time. New commit needed.
  2. First re-trigger commit had skip:test:long_running in the body (explaining what was being removed). The CI's naive contains() check matched the substring and skipped long_running again. Self-own.
  3. Clean commit 033e3e16 with no skip: substring anywhere finally let long_running actually execute.

Long_running ran for ~11 min on ubuntu-latest, false (the only OS that uploads to codecov) and pulled project coverage from 63.83% → 74.49% (+10.66 pp).

Cumulative state of this PR

  • 6 commits added by me total: c0c36706, 372d0cfd, 7f371463, 4097283e, c082df50, 033e3e16 — covering: SonarQube S1192 fix, requirements files (SHR/SWR-PLATFORM-1), SonarQube S7632 fix, Windows timing-flake fix, and two CI re-triggers.
  • All static analysis: ✅
  • All test runners green or trending: ✅ (4 confirmed pass, macos-15-intel pending)
  • Codecov: ✅ both patch (98.68%) and project (74.49%)

What's left

Only the human approval gates: PM + Tech Lead + QM approvals on PYSDK-112 before the CR can move to Closed and this PR can merge. CR ownership is on @akunft.

PYSDK-112 Impact of Change updated to reflect the codecov path and the bundled flake fix.


🤖 Posted by Claude Code executing the qms:cc-sop-01 skill (from the qms plugin) | model: claude-opus-4-7

Copilot AI review requested due to automatic review settings April 27, 2026 15:27
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Comment thread specifications/SPEC_PLATFORM_SERVICE.md
Comment thread specifications/SPEC_PLATFORM_SERVICE.md
Comment thread src/aignostics/platform/CLAUDE.md
@akunft akunft changed the title feat(platform): support external token providers and simplify caching feat(platform): support external token providers and simplify caching [PYSDK-123] May 5, 2026
@akunft akunft added the skip:test:long_running Skip long-running tests (≥5min) label May 5, 2026
@akunft akunft self-assigned this May 5, 2026
akunft pushed a commit that referenced this pull request May 5, 2026
…ows [PYSDK-112]

`test_no_retry_on_other_jwk_errors` had two assertions checking the same
property (no retry occurred):

  1. `assert elapsed_time < 2.0` — wall-clock heuristic, flaky
  2. `assert call_count == 1` — direct check on the mock's call count

Assertion 2 proves "no retry" rigorously and deterministically. Assertion
1 was a redundant proxy that consistently failed on `windows-latest`
GitHub-hosted runners (came in at ~2.4s vs the 2s threshold) while the
other four runner combos (ubuntu-latest, ubuntu-24.04-arm, macos-latest,
macos-15-intel) all completed in well under 2s.

Removing assertion 1 (and the now-unused `start_time`/`elapsed_time`
locals) removes the flake without losing test coverage. The companion
test `test_jwk_connection_errors_trigger_retry` immediately above already
follows this pattern (call-count assertion only, no wall-clock check).

Bundled into PYSDK-112 at user direction — surfaced on PR #513 CI run
and observed as the only blocker for the otherwise-green test matrix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
akunft pushed a commit that referenced this pull request May 5, 2026
CI run 24957873268 was triggered by the merge commit d9ab794 BEFORE
the skip:test:long_running label was removed from PR #513. As a result,
the workflow snapshotted the label as still present and skipped the
long-running test stage on every matrix combo (each long_running step
took 0s and was a no-op). Without long_running tests running, codecov
gathered partial coverage and codecov/project failed.

This empty commit forces a fresh workflow trigger that picks up the
current label set (skip:test:long_running no longer present), so
long_running tests will actually run and codecov gets full coverage data.
@akunft akunft force-pushed the feat/enable_external_token_provider branch from 888a147 to 975e526 Compare May 5, 2026 10:08
Add a `token_provider` parameter to `Client.__init__()` so callers can
supply their own bearer-token callable (e.g. for M2M / service-account
flows) instead of relying on the built-in OAuth device-code flow.

The operation cache needs a token to build per-user cache keys. Three
approaches were explored:

1. **CachedApiMixin with Protocol** (explored, discarded) —
   A `_HasTokenProvider` Protocol plus `CachedApiMixin` base class that
   provided a `_cached()` convenience method. Saved one kwarg per call
   site but added multiple-inheritance, a Protocol, and competing `_api`
   annotations on every resource class. Over-engineering for what amounts
   to avoiding `token_provider=self._api.token_provider`.

2. **`TokenProvider` type alias** (explored, discarded) —
   `TokenProvider = Callable[[], str]` exported as public API. Added no
   value over the raw `Callable[[], str]` since every parameter is
   already named `token_provider`. Removed to avoid unnecessary imports
   and indirection.

3. **`_AuthenticatedApi` subclass + explicit `token_provider` at each
   call site** (chosen) — A thin `_AuthenticatedApi(PublicApi)` subclass
   in `_api.py` lifts `token_provider` from the deeply-nested codegen
   `Configuration` to a top-level attribute. Each cached method passes
   `token_provider=self._api.token_provider` to `@cached_operation(...)`.
   One kwarg of boilerplate per call site, but no new types, no
   inheritance, and trivially greppable. `_api.py` exists solely to
   break the circular import between `_client.py` and the resource
   modules.
Copilot AI review requested due to automatic review settings May 5, 2026 12:43
@akunft akunft force-pushed the feat/enable_external_token_provider branch from 975e526 to 36ac55c Compare May 5, 2026 12:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

tests/aignostics/platform/resources/runs_test.py:731

  • The unit test coverage for artifact download behavior appears to have been removed from this module (e.g., Artifact.get_download_url redirect/status handling and Run.ensure_artifacts_downloaded download/resume/checksum logic), but the corresponding production code in src/aignostics/platform/resources/runs.py still exists. Please reintroduce these tests (or move them to a new dedicated test module) so changes to /file URL resolution, retry/error mapping, and download-resume behavior remain covered at the unit level (not only via slower e2e/integration tests).
@pytest.mark.unit
def test_run_details_does_not_retry_other_exceptions(app_run, mock_api) -> None:
    """Test that the outer retry does not catch non-NotFoundException errors.

    This verifies that exceptions like ForbiddenException pass straight through
    the outer retry without being retried.

    Args:
        app_run: Run instance with mock API.
        mock_api: Mock ExternalsApi instance.
    """
    from aignx.codegen.exceptions import ForbiddenException

    mock_api.get_run_v1_runs_run_id_get.side_effect = ForbiddenException()

    with pytest.raises(ForbiddenException):
        app_run.details()

    assert mock_api.get_run_v1_runs_run_id_get.call_count == 1

Copilot AI review requested due to automatic review settings May 5, 2026 20:40
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 5, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 2 comments.

Comment on lines +113 to +118
if not isinstance(api, _AuthenticatedApi): # runtime guard for untyped callers
msg = ( # type: ignore[unreachable]
f"{type(self).__name__} requires _AuthenticatedApi, "
f"got {type(api).__name__!r}. "
"Use Client to obtain a correctly configured instance."
)
Comment on lines +94 to +109
class Artifact(_AuthenticatedResource):
"""Represents a single output artifact belonging to a run.

Provides operations to resolve a fresh presigned download URL via the
``GET /api/v1/runs/{run_id}/artifacts/{artifact_id}/file`` endpoint.
"""

def __init__(self, api: PublicApi, run_id: str, artifact_id: str) -> None:
def __init__(self, api: _AuthenticatedApi, run_id: str, artifact_id: str) -> None:
"""Initializes an Artifact instance.

Args:
api (PublicApi): The configured API client.
api (_AuthenticatedApi): The configured API client.
run_id (str): The ID of the parent run.
artifact_id (str): The ID of the output artifact.
"""
self._api = api
super().__init__(api)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope:sdk-consumers Affects downstream SDK consumers (uvx aignostics / uv add aignostics) skip:test:long_running Skip long-running tests (≥5min) sop:cc-sop-01 CC-SOP-01 Change Control (feature / planned change) type:feature New functionality (conventional feat)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants